<?php
/**
 * 
 * @author yusaint
 * @since 2014-3-9
 * @project Pfinal
 */
class Pfinal_Model_SelectorChain implements Iterator,Countable{
	
	protected $joinStack=array();
	protected $stackPosition=0;
	protected $offset=0;
	protected $count=0;
	protected $orderBy;
	protected $order;
	
	protected $filterStack=array();
	protected $filterSignatureStack=array();

	
	const OPT_PRE='pre';
	const OPT_NEXT='next';
	const OPT_SEL='selector';
	const OPT_LEFT_JOIN=' left join ';
	const OPT_INNER_JOIN=' inner join ';
	const OPT_RIGHT_JOIN=' right join ';
	const OPT_CROSS_JOIN=' cross join ';
	const OPT_FULL_JOIN=' full join ';
	const OPT_UNION=' union';
	
	/**
	 * [start description]
	 * @param  Selector $selector [description]
	 * @return Pfinal_Model_SelectorChain    [description]
	 */
	public function start(Pfinal_Model_Selector $selector){
		$selector->setPre(null);
		$selector->setNext('unkonw');
		$selector->setOn(NULL);
		$this->joinStack[]=$selector;
		return $this;
	}	
	
	/**
	 *
	 * @param DorisSelect $selector
	 * @param unknown_type $opt
	 */
	protected function join(Pfinal_Model_Selector $selector,$opt,$on = null){
		$selector->setPre($opt);
		$selector->setNext(null);
		$selector->setOn($on);
		$this->joinStack[]=$selector;
	}
	/**
	 * [leftJoin description]
	 * @param  Selector $selector [description]
	 * @return Pfinal_Model_SelectorChain           [description]
	 */
	public function leftJoin(Pfinal_Model_Selector $selector,$on = null){
		$this->join($selector,self::OPT_LEFT_JOIN,$on);
		return $this;
	}
	
	public function innerJoin(Pfinal_Model_Selector $selector,$on = null){
		$this->join($selector,self::OPT_INNER_JOIN,$on);
		return $this;
	}
	/**
	 * [union description]
	 * @param  Selector $selector [description]
	 * @return [type]             [description]
	 */
	public function union($selectors,$on = null){
		if($selectors instanceof Pfinal_Model_Selector){
			$this->join($selectors,self::OPT_UNION,$on);
		}else{
			foreach ($selectors as $selector) {
				$this->join($selector,self::OPT_UNION,$on);
			}
		}
		return $this;
	}
	/**
	 * [fullJoin description]
	 * @param  Selector $selector [description]
	 * @return [type]             [description]
	 */
	public function fullJoin(Pfinal_Model_Selector $selector,$on = null){
		$this->join($selector,self::OPT_FULL_JOIN,$on);
		return $this;
	}
	/**
	 * 通常建议这里只添加afterQueryInterceptor
	 * [addFilter description]
	 * @param BaseInterceptor $filter [description]
	 */
	public function addFilter(BaseInterceptor $filter){
		$signature=$filter->signature();
		if(!in_array($signature, $this->filterSignatureStack)){
			array_push($this->filterStack, $filter);
			array_push($this->filterSignatureStack, $signature);
		}
		return $this;
	}

	/**
	 * [removeFilter description]
	 * @param  [type] $signature [description]
	 * @return [type]            [description]
	 */
	public function removeFilter($signature){
		$pos=array_search($signature, $this->filterSignatureStack);
		if($pos===null)
			return false;
		else{
			$target=$this->filterStack[$pos];
			if($target->signature()===$signature){
				unset($this->filterStack[$pos]);
			}else{
				return false;
			}
		}
		return true;
	}
	
/**
	 * @param unknown_type $count
	 * @param unknown_type $offset
	 * @throws StatementException
	 */
	public function limit($count, $offset = null) {
		if (isset($count))
			$this->count = $count;
		if (isset($offset) && !isset($count)) {
			$errMsg=sprintf('ilegal offset or count value given');
			throw new Pfinal_Exception_Runtime($errMsg);
		}
		if (isset($offset))
			$this->offset = $offset;
		return $this;
	}
	/**
	 * [orderBy description]
	 * @param  [type] $orderBy [description]
	 * @param  string $order   [description]
	 * @return Pfinal_Model_SelectorChain          [description]
	 */
	public function orderBy($orderBy,$order='desc'){
		if (!empty($orderBy)) {
			$this->orderBy=trim($orderBy);
			$this->order=trim($order);
		}
		return $this;
	}
	
	/**
	 * @return the $reportRequest
	 */
	public function getReportRequest() {
		return $this->reportRequest;
	}

	/**
	 * @param field_type $reportRequest
	 */
	public function setReportRequest($reportRequest) {
		$this->reportRequest = $reportRequest;
	}

	/**
	 * @return the $order
	 */
	public function getOrder() {
		return $this->order;
	}

	/**
	 * @return the $offset
	 */
	public function getOffset() {
		return $this->offset;
	}

	/**
	 * @return the $count
	 */
	public function getCount() {
		return $this->count;
	}

	/**
	 * @return the $orderBy
	 */
	public function getOrderBy() {
		return $this->orderBy;
	}

	/**
	 * @param number $offset
	 */
	public function setOffset($offset) {
		$this->offset = $offset;
	}

	/**
	 * @param number $count
	 */
	public function setCount($count) {
		$this->count = $count;
	}

	/**
	 * @param multitype: $orderBy
	 */
	public function setOrderBy($orderBy) {
		$this->orderBy = $orderBy;
	}
	/**
	 * [setOrder description]
	 * @param [type] $order [description]
	 */
	public function setOrder($order){
		$this->order = $order;
	}

	public function current(){
		return current($this->joinStack);
	}	
	
	/**
	 * @return the $filterStack
	 */
	public function getFilterStack() {
		return $this->filterStack;
	}

	/**
	 * @param multitype: $filterStack
	 */
	public function setFilterStack($filterStack) {
		$this->filterStack = $filterStack;
	}
	/**
	 * [next description]
	 * @return function [description]
	 */
	public function next(){
		++$this->stackPosition;
		return next($this->joinStack);
	}
	/**
	 * [key description]
	 * @return [type] [description]
	 */
	public function key(){
		return key($this->joinStack);
	}
	/**
	 * [valid description]
	 * @return [type] [description]
	 */
	public function valid(){
		return isset($this->joinStack[$this->stackPosition])?true:false;
	}
	/**
	 * [rewind description]
	 * @return [type] [description]
	 */
	public function rewind(){
		$this->stackPosition=0;
		reset($this->joinStack);
	}
	/**
	 * [count description]
	 * @return [type] [description]
	 */
	public function count(){
		return count($this->joinStack);		
	}
}

?>